﻿#define DoTrace
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using System.Reflection.Emit;
using System.Xml;

namespace Reflection
{
    class Program
    {
        static void BasicReflection()
        {
            /* Car c = new Car();
           Type t = c.GetType();*/


            //Get type
            Type t = Type.GetType("Reflection.Car", false, true);
            Type tbaseType = t.GetTypeInfo().BaseType;
            Assembly assembly = t.GetTypeInfo().Assembly;
            bool isPublic = t.GetTypeInfo().IsPublic;

            //Create Array
            Type cubeType = typeof(int).MakeArrayType(3);
            Console.WriteLine(cubeType == typeof(int[, ,]));
            int rank = typeof(int[, ,]).GetArrayRank();

            //get type
            SportsCar c = new SportsCar();
            Type s = c.GetType();

            Console.WriteLine(t.FullName);
            Console.WriteLine(t.Name);
            Console.WriteLine(t.Namespace);
            Console.WriteLine(t.UnderlyingSystemType);
            Console.WriteLine(tbaseType);
            Console.WriteLine(assembly);
            Console.WriteLine(isPublic);
            Console.WriteLine(rank);


            Console.WriteLine("get type array");
            Console.WriteLine(typeof(int[]).Name);
            Console.WriteLine(typeof(int[,]).Name);
            Console.WriteLine(typeof(int[,]).FullName);

            Console.WriteLine(typeof(int[]).GetElementType());

            foreach (Type s1 in typeof(System.Environment).GetNestedTypes())
            {
                Console.WriteLine(s1.FullName);
            }

            Type a = typeof(System.Environment.SpecialFolder);
            Console.WriteLine(a.Namespace);
            Console.WriteLine(a.Name);
            Console.WriteLine(a.FullName);


            //get type generic
            Type g = typeof(Dictionary<,>);
            Console.WriteLine(g.Name);
            Console.WriteLine(g.FullName);

            Type base1 = typeof(System.String).BaseType;
            Type base2 = typeof(System.IO.FileStream).BaseType;
            Console.WriteLine(base1.Name); // Object
            Console.WriteLine(base2.Name); // Stream

            foreach (Type iType in typeof(Guid).GetInterfaces())
            {
                Console.WriteLine(iType.Name);
                //Console.WriteLine("sd");
            }


            //Refaction provide two dynamic equavalents to C#'s static is operator
            object obj = Guid.NewGuid();
            Type target = typeof(IFormattable);

            bool isTrue = obj is IFormattable;
            bool alsoTrue = target.IsInstanceOfType(obj);

            Console.WriteLine(isTrue);
            Console.WriteLine(alsoTrue);


            //Instantiating Types
            int i = (int)Activator.CreateInstance(typeof(int));
            DateTime dt = (DateTime)Activator.CreateInstance(typeof(DateTime), 2000, 1, 1);
            Console.WriteLine(i);
            Console.WriteLine(dt);

            //invoke on a constructorInfo
            // ConstructorInfo ci = typeof (X).GetConstructor(new[] {typeof (string)});
            // object foo = ci.Invoke(new object[] {null});

            Type closed = typeof(List<int>);
            List<int> list = (List<int>)Activator.CreateInstance(closed);
            list.Add(1);
            Console.WriteLine(list[0]);

            Type unbound = typeof(List<>);
            Type close = unbound.MakeGenericType(typeof(int));
            //object anError = Activator.CreateInstance(unbound);

            //the GetGenericTypeDefinition method does the opposite
            Type unbound2 = close.GetGenericTypeDefinition();

            //Reflecting and Invoking Memble
            Console.WriteLine("Reflecting and Invoking Memble");

            MemberInfo[] member = typeof(Car).GetMembers();
            foreach (MemberInfo memberInfo in member)
            {
                Console.WriteLine(memberInfo);
            }

            Console.WriteLine();
            IEnumerable<MemberInfo> members = typeof(Car).GetTypeInfo().DeclaredMethods;
            foreach (var memberInfo in members)
            {
                Console.WriteLine(memberInfo);
            }

            //Generic type member
            PropertyInfo unboundList = typeof(List<>).GetProperty("Count");
            PropertyInfo closedList = typeof(List<int>).GetProperty("Count");
            Console.WriteLine(unboundList); // Int32 Count
            Console.WriteLine(closedList); // Int32 Count

            //Dynamically invoking a Member
            object str = "Vinh";
            PropertyInfo prop = str.GetType().GetProperty("Length");
            int length = (int)prop.GetValue(str, null);
            Console.WriteLine(length);



            Console.WriteLine();
            Console.WriteLine();


            Console.WriteLine(s.FullName);
            Console.WriteLine(s.UnderlyingSystemType);
            Console.WriteLine(s.IsArray);
            //Console.WriteLine(t.GetConstructor());

            Console.ReadLine();
        }

        static void WorkingAttributes()
        {
            TypeAttributes ta = typeof (Console).Attributes;
            MethodAttributes ma = MethodInfo.GetCurrentMethod().Attributes;
            Console.WriteLine(ta +"\r\n"+ma);
        }

        static void CodeGeneration()
        {
            var dynMeth = new DynamicMethod("Foo", null, null, typeof (Program));
            ILGenerator gen = dynMeth.GetILGenerator();
            
            //MemberInfo privateMethod = typeof (Program).GetMethod("Hello world",
            //    BindingFlags.Static | BindingFlags.NonPublic);

            //gen.Emit(OpCodes.Call, privateMethod);
            //gen.Emit(OpCodes.Ret);



            // gen.EmitWriteLine("Hello world");
             MethodInfo writelineStr = typeof (Console).GetMethod("WriteLine", new Type[] {typeof (string)});
             gen.Emit(OpCodes.Ldstr,"Hello world");
             gen.Emit(OpCodes.Call, writelineStr);
             gen.Emit(OpCodes.Ret);

            dynMeth.Invoke(null, null);
        }

        static void Assembly()
        {
            Assembly a = typeof (Program).Assembly;
            Console.WriteLine(a.FullName);
            Console.WriteLine(a.Location);
            Console.WriteLine(a.GetModules());
        }

        static void EvalutionStack()
        {
            var dynMeth = new DynamicMethod("Foo", null, null, typeof (void));
            ILGenerator gen = dynMeth.GetILGenerator();
            MethodInfo writeLineInt = typeof (Console).GetMethod("WriteLine", new[] {typeof (int)});

            gen.Emit(OpCodes.Ldc_I4,123);
            gen.Emit(OpCodes.Ldc_I4,2);
            gen.Emit(OpCodes.Add);
            gen.Emit(OpCodes.Call,writeLineInt);

            gen.Emit(OpCodes.Ret);
            dynMeth.Invoke(null, null);

            DynamicMethod dynMeth2 = new DynamicMethod("Foo",
                typeof(int),
                new []{typeof(int),typeof(int)},
                typeof(void));
            ILGenerator gen2 = dynMeth2.GetILGenerator();
            gen2.Emit(OpCodes.Ldarg_0);
            gen2.Emit(OpCodes.Ldarg_1);
            gen2.Emit(OpCodes.Add);
            gen2.Emit(OpCodes.Ret);

            int result = (int) dynMeth2.Invoke(null, new object[] {3, 4});

            //Delegate int BinaryFunction
            //(int n1, int n2);
            Console.WriteLine(result);

            var dynMeth3 = new DynamicMethod("Program", null, null, typeof (void));
            ILGenerator gen3 = dynMeth3.GetILGenerator();

            ConstructorInfo ci = typeof (StringBuilder).GetConstructor(new Type[0]);
            gen.Emit(OpCodes.Newobj,ci);


        }

        static void EmitAssembly()
        {
            AppDomain appDomain = AppDomain.CurrentDomain;
            AssemblyName aname = new AssemblyName("MyDynamicAssemble");

            AssemblyBuilder assemBuilder = appDomain.DefineDynamicAssembly(aname, AssemblyBuilderAccess.Run);
            ModuleBuilder modBuilder = assemBuilder.DefineDynamicModule("DynModule");

            TypeBuilder tb = modBuilder.DefineType("Widget", TypeAttributes.Public);

            //MethodBuilder methBuilder = tb.DefineMethod("SayHello", MethodAttributes.Public, null, null);
            //ILGenerator gen = methBuilder.GetILGenerator();
            //gen.EmitWriteLine("Hello World");
            //gen.Emit(OpCodes.Ret);

            //Type t = tb.CreateType();
            //object o = Activator.CreateInstance(t);
            //t.GetMethod("SayHello").Invoke(o, null);

            MethodBuilder mb = tb.DefineMethod("SquareRoot",
                MethodAttributes.Static | MethodAttributes.Public,
                CallingConventions.Standard,
                typeof (double),//Return type
                new[] {typeof (double)});//Parameter Types

            mb.DefineParameter(1, ParameterAttributes.None, "value");

            ILGenerator gen = mb.GetILGenerator();
            gen.Emit(OpCodes.Ldarg_0);
            gen.Emit(OpCodes.Call,typeof(Math).GetMethod("Sqrt"));
            gen.Emit(OpCodes.Ret);

            Type realType = tb.CreateType();
            double x = (double) tb.GetMethod("SquareRoot").Invoke(null, new object[] {10.0});
            Console.WriteLine(x);
        }

        static void SavingEmitAssembly()
        {
            AppDomain appDomain = AppDomain.CurrentDomain;
            AssemblyName aname = new AssemblyName("MyDynamicAssemble");

            AssemblyBuilder assemBuilder = appDomain.DefineDynamicAssembly(aname, AssemblyBuilderAccess.RunAndSave,@"d:\assemblies");
            ModuleBuilder modBuilder = assemBuilder.DefineDynamicModule("DynModule","MyEmissions.dll"); 

            TypeBuilder tb = modBuilder.DefineType("Widget", TypeAttributes.Public);

            MethodBuilder methBuilder = tb.DefineMethod("SayHello", MethodAttributes.Public, null, null);

            ILGenerator gen = methBuilder.GetILGenerator();

            gen.EmitWriteLine("Hello World");
            gen.Emit(OpCodes.Ret);

            Type t = tb.CreateType();
            object o = Activator.CreateInstance(t);
            t.GetMethod("SayHello").Invoke(o, null);
            assemBuilder.Save("MyEmissions.dll");
        }


        static void AttachingAttributes()
        {
           // 
        }

        static void DemoAssambly()
        {
            Assembly a = typeof (Car).Assembly;
            Console.WriteLine(a.FullName);
            Console.WriteLine(a.Location);
            Console.WriteLine();
            AssemblyName assName = typeof (Car).Assembly.GetName();
            Console.WriteLine(assName.GetPublicKey());
            Console.WriteLine(assName.Version);
        }

        static void DemoAttributes()
        {
            DemoAttributes attributes = new DemoAttributes();

            attributes.PrintOut("Vinh");

            attributes.TraceMessage("Start of the Main");
            Console.WriteLine("To do Main");
            attributes.TraceMessage("Final of the Main");

            attributes.MyTrace("Simple message");

            Console.WriteLine();
        }

        public static void GetCusAttributes()
        {
            Type t = typeof (Car);
            object[] AttArr = t.GetCustomAttributes(false);
            foreach (Attribute a in AttArr)
            {
                ReviewCommentAttribute attr = a as ReviewCommentAttribute;
                if (null != attr)
                {
                    Console.WriteLine("Description:      {0}",attr.Description);
                    Console.WriteLine("Version:          {0}",attr.VersionNumber);
                    Console.WriteLine("ReviewID:         {0}",attr.ReviewID);
                }
            }
        }

        static void Main(string[] args)
        {
          //DemoAttributes();
          //GetCusAttributes();
          //BasicReflection();
          //WorkingAttributes();
          //CodeGeneration();
          //Assembly();
          //EvalutionStack();
          //EmitAssembly();
          //SavingEmitAssembly();
          DemoAssambly();
        }
    }
}
